﻿#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
using namespace std;

//2.1多态的构成条件
//继承中要构成多态还有两个条件：
//1.必须通过基类的指针或者引用调用虚函数
//2.被调用的函数必须是虚函数，且派生类必须对基类的虚函数进行重写

//2.2 虚函数
//虚函数：即被virtual修饰的类成员函数称为虚函数。

//2.3虚函数的重写
//虚函数的重写(覆盖)：派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的
//返回值类型、函数名字、参数列表完全相同)，称子类的虚函数重写了基类的虚函数
//class Person {
//public:
//	virtual void BuyTicket() { cout << "Person买票-全价" << endl; }
//};
//
//class Student : public Person {
//public:
//	virtual void BuyTicket() { cout << "Student买票-半价" << endl; }
//	//注意：在重写基类虚函数时，派生类的虚函数在不加virtual关键字时，虽然也可以构成重写
//	//(因为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范，不建议这样使用
//	//void BuyTicket() { cout << "买票-半价" << endl; }
//};
//
//void Func(Person& p) { p.BuyTicket(); }
//
//int main() {
//	Person ps;
//	Student st;
//	//函数参数是父类的引用，所以既可以传父类对象，也可以传子类对象
//	Func(ps);//Person买票-全价
//	Func(st);//Student买票-半价
//	return 0;
//}



//虚函数重写的两个例外：
//1. 协变(基类与派生类虚函数返回值类型不同)
//派生类重写基类虚函数时，与基类虚函数返回值类型不同。
//即基类虚函数返回基类对象的指针或者引用，派生类虚函数返回派生类对象的指针或者引用时，称为协变。（了解）
//class Person {
//public:
//	virtual Person* BuyTicket() { cout << "Person买票-全价" << endl; }
//};
//
//class Student : public Person {
//public:
//	virtual Student* BuyTicket() { cout << "Student买票-半价" << endl; }
//};
//
//class A {};
//class B : public A {};
//class Person {
//public:
//	virtual A* f() { return new A; }
//};
//
//class Student : public Person {
//public:
//	virtual B* f() { return new B; }
//};

//还有一种例外：子类重写的虚函数可以不加virtual。(建议加上)
//可以理解为子类继承了父类的虚函数，重写是对虚函数的实现重写

//2.析构函数的重写(基类与派生类析构函数的名字不同)
//如果基类的析构函数为虚函数，此时派生类析构函数只要定义，无论是否加virtual关键字，
//都与基类的析构函数构成重写，虽然基类与派生类析构函数名字不同。虽然函数名不相同，
//看起来违背了重写的规则，其实不然，这里可以理解为编译器对析构函数的名称做了特殊处理，
//编译后析构函数的名称统一处理成destructor。
//class Person {
//public:
//	~Person() { cout << "~Person()" << endl; }
//};
//
//class Student : public Person {
//public:
//	~Student() { cout << "~Student()" << endl; }
//};
//
//int main() {
//	//下方delete调用的都是父类的析构函数
//	//delete是先调用析构函数，在调用operator delete释放。
//	//由于多态的原因，父子类的析构函数名都被统一处理为destructor。
//	//p是Person类型指针，所以指向父类的析构函数。
//	//但父类指针有可能指向父类，也有可能指向子类，期望是多态调用
//	Person* p = new Person;
//	delete p;
//
//	p = new Student;
//	delete p;
//
//	return 0;
//}

//只有派生类Student的析构函数重写了Person的析构函数，下面的delete对象调用析构函数，
//才能构成多态，才能保证p1和p2指向的对象正确的调用析构函数。
//class Person {
//public:
//	virtual ~Person() { cout << "~Person()" << endl; }
//};
//class Student : public Person {
//public:
//	virtual ~Student() { cout << "~Student()" << endl; }
//	//子类重写父类虚函数可以不加virtual就是为这里准备的
//};
//
//int main() {
//	//析构函数需要改为虚函数是为了解决new/delete场景下析构对象不正确
//	//Person* p1 = new Person;
//	//Person* p2 = new Student;
//	//delete p1;
//	//delete p2;
//
//	Person* p = new Person;
//	delete p;
//	p = new Student;
//	delete p;
//	return 0;
//}



//面试题
//class A {
//public:
//	virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; }
//	virtual void test() { func(); }
//};
//
//class B : public A {
//public:
//	void func(int val = 0) { std::cout << "B->" << val << std::endl; }
//};
//
//int main(int argc, char* argv[]) {
//	B* p = new B;
//	p->test();
//	//p->func();//B->0
//	//不构成多态，不是父类指针调用。所以调用B类的func()
//	return 0;
//}
//程序输出结果是什么（）
//A: A->0  B: B->1  C: A->1  D: B->0  E:编译出错  F:以上都不正确
//答案：B

//B公有继承A，因此B获得了A的所有成员（包括 test() 方法）。
//p是B*类型，但test()是A的非虚成员函数，因此直接调用A::test()。
//关于test()中隐藏的 this 指针：
//静态类型（编译期类型）：A*（因为 test() 是 A 的成员函数）。
//动态类型（运行时类型）：B*（因为 p 指向 B 对象）。
//所以test函数中的func函数是B在调用。
//而func()是虚函数，且this的静态类型是A*，动态类型是B*，完全满足多态条件。
//所以实际是在调用B类的func。
//但默认参数值根据静态类型（A*）决定，使用A中定义的默认值1，所以最后结果是B->1。
//或可以理解为子类继承父类虚函数是继承该接口，重写是对实现重写。所以缺省参数用父类的。

//多态的发生是因为A::test()内部的 this->func()满足“基类指针调用虚函数”的条件（this的静态类型是 A*，动态类型是B* ）。
//虽然 p 是 B* ，但真正触发多态的是成员函数内部的 this 指针。



//2.4 C++11 override 和 final
//1.final：修饰虚函数，表示该虚函数不能再被重写
//final修饰类，不能被继承
//final修饰虚函数，不能被重写
//class Car {
//public:
//	//void Drive() final {} //非虚函数不能用final修饰
//	virtual void Drive() final {}
//};
//class Benz :public Car {
//public:
//	virtual void Drive() { cout << "Benz-舒适" << endl; }
//};

//2.override：检查派生类虚函数是否重写了基类某个虚函数，如果没有重写编译报错。